/*
Copyright (C) 2008 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

DataSource.NETWORK_CHECK_INTERVAL_MS = 1 * 60 * 1000;
DataSource.TIMEOUT_MS = 20 * 1000;

function DataSource(url, interval, callback) {
  this.url = url;
  this.interval = interval;
  this.callback = callback;

  this.timeoutTimer = null;
  this.retryCount = 0;
  this.data = '';
  this.isReady = false;

  this.retryTimer = null;

  this.request = new XMLHttpRequest();

  this.send();
}

DataSource.prototype.onReadyStateChange = function() {
  if (this.request.readyState != 4) {
    return;
  }

  if (this.request.status == 200) {
    debug.trace('Retrieve succeeded.');
    debug.trace('Cancelling request timeout timer.');
    this.clearTimeout();

    this.retryCount = 0;
    this.data = this.request.responseText;
    this.isReady = true;
    this.callback(this.data);

    this.scheduleNext();
  } else {
    debug.error('Request error. Status: ' + this.request.status);
  }
};

DataSource.prototype.makeOnReadyStateChange = function() {
  var me = this;

  return function() {
    me.onReadyStateChange();
  };
};

DataSource.prototype.clearRequest = function() {
  this.clearTimeout();
  this.request.abort();
};

DataSource.prototype.clearTimeout = function() {
  if (this.timeoutTimer) {
    view.clearTimeout(this.timeoutTimer);
    this.timeoutTimer = null;
  }
};

DataSource.prototype.onFail = function() {
  ++this.retryCount;
  debug.trace('Request failure #' + this.retryCount);

  // Clear current request.
  this.clearRequest();

  if (!this.isReady) {
    // Don't have valid data, retry with exponential backoff.
    this.scheduleExpoRetry();
    return;
  }

  // Retry normally.
  this.scheduleNext();
};

DataSource.prototype.scheduleNext = function() {
  this.clearNextTimer();
  debug.trace('Retry request in ' + this.interval);
  this.nextTimer = view.setTimeout(this.makeSend(), this.interval);
};

DataSource.prototype.scheduleNetworkCheck = function() {
  this.clearNextTimer();
  debug.trace('Retry request in ' + DataSource.NETWORK_CHECK_INTERVAL_MS);
  this.nextTimer = view.setTimeout(this.makeSend(),
      DataSource.NETWORK_CHECK_INTERVAL_MS);
};

DataSource.prototype.scheduleExpoRetry = function() {
  this.clearNextTimer();

  var nextRetryMs = Math.pow(2, this.retryCount) * 60 * 1000;

  if (nextRetryMs > this.interval) {
    nextRetryMs = this.interval;
  }

  debug.trace('Retry request in ' + nextRetryMs);

  this.nextTimer = view.setTimeout(this.makeSend(), nextRetryMs);
};

DataSource.prototype.clearNextTimer = function() {
  if (this.nextTimer) {
    view.clearTimeout(this.nextTimer);
    this.nextTimer = null;
  }
};

DataSource.prototype.makeOnFail = function() {
  var me = this;

  return function() {
    me.onFail();
  };
};

DataSource.prototype.makeSend = function() {
  var me = this;

  return function() {
    me.send();
  };
};

// Will abort any pending request.
DataSource.prototype.send = function() {
  debug.trace('Sending request.');

  // Clear previous request.
  this.clearRequest();

  // Check if network is online.
  if (!framework.system.network.online) {
    debug.trace('Machine is not online.');
    // If not, retry later.
    this.scheduleNetworkCheck();
    return;
  }

  // Set timeout timer.
  this.clearTimeout();
  this.timeoutTimer = view.setTimeout(this.makeOnFail(), DataSource.TIMEOUT_MS);

  this.request.onreadystatechange = this.makeOnReadyStateChange();
  this.request.open('GET', this.getProxyUrl(), true);
  this.request.send();
};

DataSource.PROXY_URL = 'http://p.gmodules.com/ig/proxy';

DataSource.prototype.getProxyUrl = function() {
  return DataSource.PROXY_URL + '?url=' + encodeURIComponent(this.url) +
      '&max_age=0';
};

DataSource.prototype.reload = function() {
  this.send();
};

DataSource.prototype.read = function() {
  return this.data;
};
